home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software of the Month Club 2000 October
/
Software of the Month - Ultimate Collection Shareware 277.iso
/
pc
/
PROGRAMS
/
UTILITY
/
WINLINUX
/
DATA1.CAB
/
programs_-_kernel_source
/
SCRIPTS
/
MKDEP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1999-09-17
|
10KB
|
506 lines
/*
* Originally by Linus Torvalds.
* Smart CONFIG_* processing by Werner Almesberger, Michael Chastain.
*
* Usage: mkdep file ...
*
* Read source files and output makefile dependency lines for them.
* I make simple dependency lines for #include <*.h> and #include "*.h".
* I also find instances of CONFIG_FOO and generate dependencies
* like include/config/foo.h.
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
char __depname[512] = "\n\t@touch ";
#define depname (__depname+9)
int hasdep;
struct path_struct {
int len;
char buffer[256-sizeof(int)];
} path_array[2] = {
{ 0, "" },
{ 0, "" }
};
/*
* This records all the configuration options seen.
* In perl this would be a hash, but here it's a long string
* of values separated by newlines. This is simple and
* extremely fast.
*/
char * str_config = NULL;
int size_config = 0;
int len_config = 0;
/*
* Grow the configuration string to a desired length.
* Usually the first growth is plenty.
*/
void grow_config(int len)
{
if (str_config == NULL) {
len_config = 0;
size_config = 4096;
str_config = malloc(4096);
if (str_config == NULL)
{ perror("malloc"); exit(1); }
}
while (len_config + len > size_config) {
str_config = realloc(str_config, size_config *= 2);
if (str_config == NULL)
{ perror("malloc"); exit(1); }
}
}
/*
* Lookup a value in the configuration string.
*/
int is_defined_config(const char * name, int len)
{
const char * pconfig;
const char * plast = str_config + len_config - len;
for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) {
if (pconfig[ -1] == '\n'
&& pconfig[len] == '\n'
&& !memcmp(pconfig, name, len))
return 1;
}
return 0;
}
/*
* Add a new value to the configuration string.
*/
void define_config(int convert, const char * name, int len)
{
grow_config(len + 1);
memcpy(str_config+len_config, name, len);
if (convert) {
int i;
for (i = 0; i < len; i++) {
char c = str_config[len_config+i];
if (isupper(c)) c = tolower(c);
if (c == '_') c = '/';
str_config[len_config+i] = c;
}
}
len_config += len;
str_config[len_config++] = '\n';
}
/*
* Clear the set of configuration strings.
*/
void clear_config(void)
{
len_config = 0;
define_config(0, "", 0);
}
/*
* Handle an #include line.
*/
void handle_include(int type, const char * name, int len)
{
struct path_struct *path = path_array+type;
if (len == 14 && !memcmp(name, "linux/config.h", len))
return;
if (len >= 7 && !memcmp(name, "config/", 7))
define_config(0, name+7, len-7-2);
memcpy(path->buffer+path->len, name, len);
path->buffer[path->len+len] = '\0';
if (access(path->buffer, F_OK) != 0)
return;
if (!hasdep) {
hasdep = 1;
printf("%s:", depname);
}
printf(" \\\n %s", path->buffer);
}
/*
* Record the use of a CONFIG_* word.
*/
void use_config(const char * name, int len)
{
char *pc;
int i;
pc = path_array[0].buffer + path_array[0].len;
memcpy(pc, "config/", 7);
pc += 7;
for (i = 0; i < len; i++) {
char c = name[i];
if (isupper(c)) c = tolower(c);
if (c == '_') c = '/';
pc[i] = c;
}
pc[len] = '\0';
if (is_defined_config(pc, len))
return;
define_config(0, pc, len);
if (!hasdep) {
hasdep = 1;
printf("%s: ", depname);
}
printf(" \\\n $(wildcard %s.h)", path_array[0].buffer);
}
/*
* Macros for stunningly fast map-based character access.
* __buf is a register which holds the current word of the input.
* Thus, there is one memory access per sizeof(unsigned long) characters.
*/
#if defined(__alpha__) || defined(__i386__) || defined(__arm__)
#define LE_MACHINE
#endif
#ifdef LE_MACHINE
#define next_byte(x) (x >>= 8)
#define current ((unsigned char) __buf)
#else
#define next_byte(x) (x <<= 8)
#define current (__buf >> 8*(sizeof(unsigned long)-1))
#endif
#define GETNEXT { \
next_byte(__buf); \
if ((unsigned long) next % sizeof(unsigned long) == 0) { \
if (next >= end) \
break; \
__buf = * (unsigned long *) next; \
} \
next++; \
}
/*
* State machine macros.
*/
#define CASE(c,label) if (current == c) goto label
#define NOTCASE(c,label) if (current != c) goto label
/*
* Yet another state machine speedup.
*/
#define MAX2(a,b) ((a)>(b)?(a):(b))
#define MIN2(a,b) ((a)<(b)?(a):(b))
#define MAX6(a,b,c,d,e,f) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,MAX2(e,f))))))
#define MIN6(a,b,c,d,e,f) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,MIN2(e,f))))))
/*
* The state machine looks for (approximately) these Perl regular expressions:
*
* m|\/\*.*?\*\/|
* m|'.*?'|
* m|".*?"|
* m|#\s*include\s*"(.*?)"|
* m|#\s*include\s*<(.*?>"|
* m|#\s*(?define|undef)\s*CONFIG_(\w*)|
* m|(?!\w)CONFIG_|
* m|__SMP__|
*
* About 98% of the CPU time is spent here, and most of that is in
* the 'start' paragraph. Because the current characters are
* in a register, the start loop usually eats 4 or 8 characters
* per memory read. The MAX6 and MIN6 tests dispose of most
* input characters with 1 or 2 comparisons.
*/
void state_machine(const char * map, const char * end)
{
const char * next = map;
const char * map_dot;
unsigned long __buf = 0;
for (;;) {
start:
GETNEXT
__start:
if (current > MAX6('/','\'','"','#','C','_')) goto start;
if (current < MIN6('/','\'','"','#','C','_')) goto start;
CASE('/', slash);
CASE('\'', squote);
CASE('"', dquote);
CASE('#', pound);
CASE('C', cee);
CASE('_', underscore);
goto start;
/* / */
slash:
GETNEXT
NOTCASE('*', __start);
slash_star_dot_star:
GETNEXT
__slash_star_dot_star:
NOTCASE('*', slash_star_dot_star);
GETNEXT
NOTCASE('/', __slash_star_dot_star);
goto start;
/* '.*?' */
squote:
GETNEXT
CASE('\'', start);
NOTCASE('\\', squote);
GETNEXT
goto squote;
/* ".*?" */
dquote:
GETNEXT
CASE('"', start);
NOTCASE('\\', dquote);
GETNEXT
goto dquote;
/* #\s* */
pound:
GETNEXT
CASE(' ', pound);
CASE('\t', pound);
CASE('i', pound_i);
CASE('d', pound_d);
CASE('u', pound_u);
goto __start;
/* #\s*i */
pound_i:
GETNEXT NOTCASE('n', __start);
GETNEXT NOTCASE('c', __start);
GETNEXT NOTCASE('l', __start);
GETNEXT NOTCASE('u', __start);
GETNEXT NOTCASE('d', __start);
GETNEXT NOTCASE('e', __start);
goto pound_include;
/* #\s*include\s* */
pound_include:
GETNEXT
CASE(' ', pound_include);
CASE('\t', pound_include);
map_dot = next;
CASE('"', pound_include_dquote);
CASE('<', pound_include_langle);
goto __start;
/* #\s*include\s*"(.*)" */
pound_include_dquote:
GETNEXT
CASE('\n', start);
NOTCASE('"', pound_include_dquote);
handle_include(1, map_dot, next - map_dot - 1);
goto start;
/* #\s*include\s*<(.*)> */
pound_include_langle:
GETNEXT
CASE('\n', start);
NOTCASE('>', pound_include_langle);
handle_include(0, map_dot, next - map_dot - 1);
goto start;
/* #\s*d */
pound_d:
GETNEXT NOTCASE('e', __start);
GETNEXT NOTCASE('f', __start);
GETNEXT NOTCASE('i', __start);
GETNEXT NOTCASE('n', __start);
GETNEXT NOTCASE('e', __start);
goto pound_define_undef;
/* #\s*u */
pound_u:
GETNEXT NOTCASE('n', __start);
GETNEXT NOTCASE('d', __start);
GETNEXT NOTCASE('e', __start);
GETNEXT NOTCASE('f', __start);
goto pound_define_undef;
/* #\s*(define|undef)\s*CONFIG_(\w*) */
pound_define_undef:
GETNEXT
CASE(' ', pound_define_undef);
CASE('\t', pound_define_undef);
NOTCASE('C', __start);
GETNEXT NOTCASE('O', __start);
GETNEXT NOTCASE('N', __start);
GETNEXT NOTCASE('F', __start);
GETNEXT NOTCASE('I', __start);
GETNEXT NOTCASE('G', __start);
GETNEXT NOTCASE('_', __start);
map_dot = next;
pound_define_undef_CONFIG_word:
GETNEXT
if (isalnum(current) || current == '_')
goto pound_define_undef_CONFIG_word;
define_config(1, map_dot, next - map_dot - 1);
goto __start;
/* \<CONFIG_(\w*) */
cee:
if (next >= map+2 && (isalnum(next[-2]) || next[-2] == '_'))
goto start;
GETNEXT NOTCASE('O', __start);
GETNEXT NOTCASE('N', __start);
GETNEXT NOTCASE('F', __start);
GETNEXT NOTCASE('I', __start);
GETNEXT NOTCASE('G', __start);
GETNEXT NOTCASE('_', __start);
map_dot = next;
cee_CONFIG_word:
GETNEXT
if (isalnum(current) || current == '_')
goto cee_CONFIG_word;
use_config(map_dot, next - map_dot - 1);
goto __start;
/* __SMP__ */
underscore:
GETNEXT NOTCASE('_', __start);
GETNEXT NOTCASE('S', __start);
GETNEXT NOTCASE('M', __start);
GETNEXT NOTCASE('P', __start);
GETNEXT NOTCASE('_', __start);
GETNEXT NOTCASE('_', __start);
use_config("SMP", 3);
goto __start;
}
}
/*
* Generate dependencies for one file.
*/
void do_depend(const char * filename, const char * command)
{
int mapsize;
int pagesizem1 = getpagesize()-1;
int fd;
struct stat st;
char * map;
fd = open(filename, O_RDONLY);
if (fd < 0) {
perror(filename);
return;
}
fstat(fd, &st);
if (st.st_size == 0) {
fprintf(stderr,"%s is empty\n",filename);
close(fd);
return;
}
mapsize = st.st_size;
mapsize = (mapsize+pagesizem1) & ~pagesizem1;
map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
if ((long) map == -1) {
perror("mkdep: mmap");
close(fd);
return;
}
if ((unsigned long) map % sizeof(unsigned long) != 0)
{
fprintf(stderr, "do_depend: map not aligned\n");
exit(1);
}
hasdep = 0;
clear_config();
state_machine(map, map+st.st_size);
if (hasdep)
puts(command);
munmap(map, mapsize);
close(fd);
}
/*
* Generate dependencies for all files.
*/
int main(int argc, char **argv)
{
int len;
char *hpath;
hpath = getenv("HPATH");
if (!hpath) {
fputs("mkdep: HPATH not set in environment. "
"Don't bypass the top level Makefile.\n", stderr);
return 1;
}
len = strlen(hpath);
memcpy(path_array[0].buffer, hpath, len);
if (len && hpath[len-1] != '/')
path_array[0].buffer[len++] = '/';
path_array[0].buffer[len] = '\0';
path_array[0].len = len;
while (--argc > 0) {
const char * filename = *++argv;
const char * command = __depname;
len = strlen(filename);
memcpy(depname, filename, len+1);
if (len > 2 && filename[len-2] == '.') {
if (filename[len-1] == 'c' || filename[len-1] == 'S') {
depname[len-1] = 'o';
command = "";
}
}
do_depend(filename, command);
}
return 0;
}